package list

// internal element of List
type node struct {
	data interface{}
	next *node
	prev *node
}

// Generic List, and doubly linked list
type List struct {
	head *node
	tail *node
}

// Init a empty List
func New() *List {
	return &List{
		head: nil,
		tail: nil,
	}

}

// Push a data into the tail of  List
func (li *List) Push(data interface{}) {
	node_inst := node{data: data, next: nil, prev: nil}
	if li.head == nil {
		li.head = &node_inst
		li.tail = &node_inst
	} else {
		li.tail.next = &node_inst
		node_inst.prev = li.tail
		li.tail = &node_inst
	}

}

// Init List using some values, maybe any is interface{}?
func FromArgs(args ...interface{}) *List {
	list_inst := New()

	for _, e := range args {
		list_inst.Push(e)

	}

	return list_inst

}

// Iterate through the list and get the elements, chan is some sort for quenen?
func (li *List) Iter() <-chan interface{} {
	ch := make(chan interface{})

	go func() {
		current := li.head
		for current != nil {
			ch <- current.data
			current = current.next
		}

		close(ch)
	}()

	return ch
}

// Clone the content of existing list, shallow copy, not deep copy
func (li *List) Clone() *List {
	new_list := New()
	for e := range li.Iter() {
		new_list.Push(e)
	}

	return new_list
}

// Check the List is Empty ?
func (li *List) IsEmpty() bool {
	return li.head == nil
}

// An alias of Iter
func (li *List) Each() <-chan interface{} {
	return li.Iter()
}

func (li *List) Len() uint {
	var count uint = 0
	current := li.head
	for current != nil {
		count++
		current = current.next
	}

	return count
}

func (li *List) isIndexOutOfRange(index uint) bool {
	if li.Len() == 0 {
		return true
	}
	if index > li.Len()-1 {
		return true // out of range
	}

	return false
}

// Get the elemnt by index, return error if index out of range
func (li *List) At(index uint) (interface{}, error) {
	if li.isIndexOutOfRange(index) {
		return nil, indexOutOfRange() // where is this method come from ?
	}

	var count uint = 0
	current := li.head
	for count < index {
		current = current.next
		count++
	}

	return current.data, nil

}

// Shift data from the head of the list, return error if empty list
func (li *List) Shift() (interface{}, error) {
	if li.head == nil {
		return nil, emptyList() // from list_error.go, this list package
	}

	if li.head == li.tail { // One element list
		data := li.head.data
		li.head = nil
		li.tail = nil

		return data, nil
	} else {
		current := li.head
		data := current.data
		li.head = current.next
		li.head.prev = nil
		current = nil

		return data, nil
	}
}

// Unshift data into the head of list
func (li *List) Unshift(data interface{}) {
	node := node{data: data, prev: nil, next: nil}
	if li.head == nil {
		li.head = &node
		li.tail = &node
	} else {
		li.head.prev = &node
		node.next = li.head
		li.head = &node
	}

}

func (li *List) Pop() (interface{}, error) {
	if li.head == nil {
		return nil, emptyList()
	}

	if li.head == li.tail {
		data := li.head.data
		// set nil once get the data
		li.head = nil
		li.tail = nil
		// end set nil

		return data, nil
	} else {
		current := li.tail
		data := current.data
		li.tail = current.prev
		li.tail.next = nil
		current = nil

		return data, nil
	}

}

func (li *List) RemoveAt(index uint) (interface{}, error) {
	if li.isIndexOutOfRange(index) {
		return nil, indexOutOfRange()
	}

	var previous *node = nil
	var current *node = li.head
	var count uint = 0
	for count < index {
		previous = current
		current = current.next
		count++
	}

	if current == li.head { // index == 0
		return li.Shift() // Shift() is like pop data?
	} else if current == li.tail { // index == Len() - 1
		return li.Pop() // pop tail element
	} else {
		data := current.data
		previous.next = current.next
		current.next.prev = previous
		current.prev = nil
		current.next = nil
		current = nil

		return data, nil
	}
}
